--[[ 
DarkPortal.net Data Collector
Copyright (c) Dynamoid Oy 2006-2007
All Rights Reserved


global variables: 	DarkPortal_Save.variablename
addon variables:		DarkPortal.variablename
addon functions:		DarkPortal:Function_Name
]]

DarkPortal_Save = DarkPortal_Save or {}
DarkPortal_Save.settings = DarkPortal_Save.settings or {}
DarkPortal_Save.mobs = DarkPortal_Save.mobs or {}
DarkPortal_Save.items = DarkPortal_Save.items or {}
DarkPortal_Save.random = DarkPortal_Save.random or {}
DarkPortal_Save.profiles = DarkPortal_Save.profiles or {}


-- local variables

DarkPortal = {
	archive = {
		items = setmetatable({}, { 
			__index = function(self, id)
				local value = DarkPortal_Archive.items[id]
				self[id] = value
				return value
			end
		}),
		random = setmetatable({}, { 
			__index = function(self, id)
				local value = DarkPortal_Archive.random[id]
				self[id] = value
				return value
			end
		}),
	},
	chatcolor = "ffedd1b4",
	stamp = nil,
	character = nil,
	quitted = nil,
	lastunit = nil,
	lastunithit = nil,
	session = {},
	lootscans = { LOOT_ITEM_SELF, LOOT_ITEM_CREATED_SELF, LOOT_ITEM_PUSHED_SELF },
	killscans = { selfkill = SELFKILLOTHER, otherkill = UNITDIESOTHER },
	combatscans = { "%s hits you for", "%s crits you for" },
}


-- general functions

local pairs = pairs
local next = next

function DarkPortal:Concat(...)
	local str = {}
	for _, v in pairs({...}) do
		if v and v ~= nil then
			table.insert(str, v)
		else
			table.insert(str, "0")
		end
	end
	local concat = table.concat(str, "^")
	return concat
end

function DarkPortal:ChatMsg(msg)
	DEFAULT_CHAT_FRAME:AddMessage(msg)
end

function DarkPortal:Get_Container(bagid, slots)
	local container = {}
	for slot = 1, slots do
		itemlink = GetContainerItemLink(bagid, slot)
		if itemlink then
			local itemid, subid, enchantid, itemname, socket1, socket2, socket3 = self:Get_Itemlink(itemlink)
			local texture, stack = GetContainerItemInfo(bagid, slot)
			if not stack or stack < 1 then stack = 1 end
			container[tostring(slot)] = self:Concat(itemid, subid, enchantid, stack, socket1, socket2, socket3)
		end
	end
	return container
end

function DarkPortal:Get_Day()
	return tonumber(date("%Y%j", time()))
end

function DarkPortal:Get_Itemlink(itemlink)
	if not itemlink then return nil end
	local _, _, itemid, enchant, socket1, socket2, socket3, temp, subid, unique, itemname = string.find(itemlink, "|c%x+|Hitem:(%d+):(%-?%d+):(%-?%d+):(%-?%d+):(%-?%d+):(%-?%d+):(%-?%d+):(%-?%d+)|h%[(.+)%]|h|r")
	if not itemid then return nil end
	itemid, subid, socket1, socket2, socket3 = tonumber(itemid), tonumber(subid), tonumber(socket1), tonumber(socket2), tonumber(socket3)
	if subid ~= 0 then
		if not (DarkPortal_Save.random[subid] or self.archive.random[subid]) then
			DarkPortalTooltip:SetHyperlink(itemlink)
			local subname = ""
			local stats = {}
			local numlines = DarkPortalTooltip:NumLines()
			for i = 1, numlines do
				local text = getglobal("DarkPortalTooltipTextLeft" .. i):GetText()
				if i == 1 then
					_, _, subname = string.find(text, " (of .+)")
				else
					if string.find(text, "^|cffffffff(%+%d+)") then
						text = string.gsub(text, "|cffffffff", "")
						text = string.gsub(text, "|r\n", "")
						table.insert(stats, text)
					end
				end
			end
			DarkPortalTooltip:ClearLines()
			if subname ~= "" then	DarkPortal_Save.random[subid] = self:Concat(subname, table.concat(stats, "\n")) end
		end
	end
	if itemid then self:Set_Item(itemid) end
	if socket1 > 0 then
		local gem, gemlink = GetItemGem(itemlink, 1)
		if gemlink then	socket1 = self:Get_Itemlink(gemlink) end
	end
	if socket2 > 0 then
		local gem, gemlink = GetItemGem(itemlink, 2)
		if gemlink then	socket2 = self:Get_Itemlink(gemlink) end
	end
	if socket3 > 0 then
		local gem, gemlink = GetItemGem(itemlink, 3)
		if gemlink then	socket3 = self:Get_Itemlink(gemlink) end
	end
	return itemid, subid, enchant, itemname, socket1, socket2, socket3
end

function DarkPortal:Get_Played_Time()
	if not self.session.played then
		RequestTimePlayed()
		return 0
	else
		return self.session.played + math.floor(self.session.session)
	end
end

function DarkPortal:Get_Profile_Data(data)
	if not self.character then return nil end
	return DarkPortal_Save.profiles[self.character][data] or {}
end

function DarkPortal:Get_QuestText(text)
	local name = UnitName("player") .. "([%s%.,:;%-%(%)%!%?'])"
	local class = UnitClass("player") .. "([%s%.,:;%-%(%)%!%?'])"
	local race = UnitRace("player") .. "([%s%.,:;%-%(%)%!%?'])"
	text = string.gsub(text, name, "<Name>%1")
	text = string.gsub(text, class, "<Class>%1")
	text = string.gsub(text, string.lower(class), "<Class>%1")
	text = string.gsub(text, race, "<Race>%1")
	text = string.gsub(text, string.lower(race), "<Race>%1")
	return text
end

function DarkPortal:Get_Scans(scans)
	for i, scan in pairs(scans) do
		scan = string.gsub(scan, "%(", "%%%(")
		scan = string.gsub(scan, "%)", "%%%)")
		scan = string.gsub(scan, "(%%s)", "%(%.%+%)")
		scan = string.gsub(scan, "(%%%d$s)", "%(%.%+%)")
		scan = string.gsub(scan, "(%%d)", "%(%%%d%+%)")
		scan = string.gsub(scan, "(%%%d$d)", "%(%%%d%+%)")
		scans[i] = scan
	end
	return scans
end

function DarkPortal:Get_Stamp()
	if not self.stamp then self.stamp = self:Get_Played_Time() end
	if self.stamp == 0 then self.stamp = self:Get_Played_Time() end
	return tonumber(self.stamp)
end

function DarkPortal:Get_Texture(texture)
	return string.gsub(texture, "(.*\\)", "")
end

function DarkPortal:Get_Tooltip()
	local lines = {}
	local numlines = DarkPortalTooltip:NumLines()
	for i = 1, numlines do
		local line 
		local text = getglobal("DarkPortalTooltipTextLeft" .. i):GetText()
		if text then line = text end
		local text = getglobal("DarkPortalTooltipTextRight" .. i):GetText()
		if text then line = line .. "\t" .. text end
		if line then 
			line = string.gsub(line, "|c%x%x%x%x%x%x%x%x", "")
			line = string.gsub(line, "|r", "")
			table.insert(lines, line)
		end
	end
	local tooltip = table.concat(lines, "\n")
	DarkPortalTooltip:ClearLines()
	return tooltip
end

function DarkPortal:Set_Profile_Data(data, content)
	if not self.character then return nil end
	DarkPortal_Save.profiles[self.character][data] = content
	return true
end

function DarkPortal:Trim(text)
  return string.gsub(text, "^%s*(.-)%s*$", "%1")
end

    
-- data functions

function DarkPortal:Archive()
	local stamp = self:Get_Stamp()
	local daylimit = self:Get_Day() - 7
	local playedlimit = stamp - 1 * 24 * 60 * 60
	-- remove mobs not seen in 7 days
	if DarkPortal_Save.mobs then
		local mobs = DarkPortal_Save.mobs
			for i, mob in pairs(mobs) do
		if mob.seen < daylimit then mobs[i] = nil end
		end
		DarkPortal_Save.mobs = mobs
		mobs = nil
	end
	-- remove sessions older than 1d played
	if DarkPortal_Save.profiles then
		local profiles = DarkPortal_Save.profiles
		for i, profile in pairs(profiles) do
			if profile.sessions then
				local sessions = profile.sessions
				for j, session in pairs(sessions) do
					if j < playedlimit then sessions[j] = nil end
				end
				profile.sessions = sessions
				profiles[i] = profile
			end
		end
		DarkPortal_Save.profiles = profiles
		profiles = nil
	end
end

function DarkPortal:Set_Arena()
	local arena = {}
	for i = 1, MAX_ARENA_TEAMS do
		local teamname, size, rating, weekplayed, weekwins, seasonplayed, seasonwins, playerplayed, rank, emblem
		local background, emblemcolor, border, team = {}, {}, {}, {}
		teamname, size, rating, weekplayed, weekwins, seasonplayed, seasonwins, playerplayed, rank, background.r, background.g, background.b, emblem, emblemcolor.r, emblemcolor.g, emblemcolor.b = GetArenaTeam(i)
		if teamname then
			for j = 1, GetNumArenaTeamMembers(i, 1) do
				local mname, mrank, mlevel, mclass, monline, mplayed, mwin, mseasonplayed, mseasonwins = GetArenaTeamRosterInfo(i, j)
				team[mname] = { 
					rank = mrank, 
					level = mlevel, 
					class = mclass, 
					played = mplayed, 
					wins = mwins, 
					seasonplayed = mseasonplayed, 
					seasonwins = mseasonwins,
				}
			end
			arena[tostring(i)] = {
				name = teamname, 
				size = size,
				rating = rating,
				weekplayed = weekplayed,
				weekwins = weekwins,
				seasonplayed = seasonplayed,
				seasonwins = seasonwins,
				playerplayed = playerplayed,
				rank = rank,
				background = background,
				emblem = emblem,
				emblemcolor = emblemcolor,
				team = team,
			}
		end
	end
	self:Set_Profile_Data("arena", arena)
end

function DarkPortal:Set_Bank()
	local bank = {}
	for i = 0, GetNumBankSlots() do
		local bagid, slots
		if i == 0 then
			bagid = BANK_CONTAINER
			slots = NUM_BANKGENERIC_SLOTS
		else
			bagid = i + NUM_BAG_FRAMES
			slots = GetContainerNumSlots(bagid)
		end
		if slots and slots > 0 then
			bank["bag" .. bagid] = self:Get_Container(bagid, slots)
		end
	end
	self:Set_Profile_Data("bank", bank)
end

function DarkPortal:Set_Buffs()
	local buffs = {}
	local i = 1
	while UnitBuff("player", i) do
		local name, rank, texture, times, duration, left = UnitBuff("player", i)
		if rank ~= "" then name = name .. " (Rank " .. rank .. ")" end
		DarkPortalTooltip:SetUnitBuff("player", i)
		buffs[name] = self:Concat(times, self:Get_Texture(texture), self:Get_Tooltip())
		i = i + 1
	end
	i = 1
	while UnitDebuff("player", i) do
		local name, rank, texture, times, type, duration, left = UnitDebuff("player", i)
		if rank ~= "" then name = name .. " (Rank " .. rank .. ")" end
		DarkPortalTooltip:SetUnitDeBuff("player", i)
		buffs[name] = self:Concat(times, self:Get_Texture(texture), self:Get_Tooltip())
		i = i + 1
	end
	self:Set_Profile_Data("buffs", buffs)
end

function DarkPortal:Set_Guild() 
	if (IsInGuild()) then
		self:Set_Profile_Data("guild", self:Concat(GetGuildInfo("player")))
	else
		self:Set_Profile_Data("guild", self:Concat(0, 0, 0))
	end
end

function DarkPortal:Set_Death()
	if self.lastunithit then 
		local lastmob = self.lastunithit
		if lastmob.msg and lastmob.msg ~= "" and (time() - lastmob.time < 10) then
			for _, scan in pairs(self.combatscans) do
				if string.find(lastmob.msg, scan) then
					local _, _, name = string.find(lastmob.msg, scan)
					if DarkPortal_Save.mobs[name] then self:Set_Session_Event("deaths", name) end
					return
				end
			end
		end
	end
end

function DarkPortal:Set_Equipment()
	local equipment = {}
	for i = 0, 19 do
		local itemlink = GetInventoryItemLink("player", i)
		if itemlink then
			local itemid, subid, enchantid, name, socket1, socket2, socket3 = self:Get_Itemlink(itemlink)
			equipment[tostring(i)] = self:Concat(itemid, subid, enchantid, 1, socket1, socket2, socket3)
		end
	end
	self:Set_Profile_Data("equipment", equipment)
end

function DarkPortal:Set_Inventory()
	local inventory = {}
	for bagid = 0, NUM_BAG_FRAMES do
		local slots = GetContainerNumSlots(bagid)
		if slots and slots > 0 then
			inventory["bag" .. bagid] = self:Get_Container(bagid, slots)
		end
	end
	self:Set_Profile_Data("inventory", inventory)
end

function DarkPortal:Set_Item(itemid)
	if not itemid or self.archive.items[itemid] then return	end
	local itemname, link, rarity, level, minlevel, itemtype, subtype, stack, loc, texture = GetItemInfo(itemid)
	if itemname then
		DarkPortalTooltip:SetHyperlink("item:" .. itemid .. ":0:0:0:0:0:0:0")
		DarkPortal_Save.items[itemid] = self:Concat(itemname, rarity, self:Get_Texture(texture), minlevel, level, itemtype, subtype, loc, stack, self:Get_Tooltip())
	end
end

function DarkPortal:Set_Kill(msg)
	local _, _, mob = string.find(msg, self.killscans.otherkill)
	local _, _, name = string.find(msg, self.killscans.selfkill)
	if mob or name then
		local bosses = self:Get_Profile_Data("bosses")
		if bosses[mob] then	boss = mob elseif bosses[name] then	boss = name	end
		if boss then
			if not bosses[boss].kills then bosses[boss].kills = 1 else bosses[boss].kills = bosses[boss].kills + 1 end
			self:Set_Profile_Data("bosses", bosses)
		end			
	end
	if not name or not DarkPortal_Save.mobs[name] then return end
	self:Set_Session_Event("kills", name)			
end

function DarkPortal:Set_Level(level)
	local levels = self:Get_Profile_Data("levels")
	levels[tostring(level)] = self:Concat(self:Get_Played_Time(), time())
	self:Set_Profile_Data("levels", levels)
end

function DarkPortal:Set_Loot(msg)
	for _, loot in pairs(self.lootscans) do
		if string.find(msg, loot) then
			local _, _, itemlink = string.find(msg, loot)
			if itemlink then
				local itemid, subid = self:Get_Itemlink(itemlink)
				if itemid then self:Set_Session_Event("loots", itemid) end
			end
			return
		end
	end
end

function DarkPortal:Set_Mob(target)
	if UnitIsPlayer(target) or (UnitReaction(target, "player") > 4 and UnitIsFriend("player", target)) or UnitIsUnit(target, "pet") or UnitPlayerControlled(target) or UnitIsDeadOrGhost(target) then return end
	local name = UnitName(target)
	local level = UnitLevel(target)
	local namelevel = self:Concat(name, level)
	if namelevel == self.lastunit or name == UNKNOWNOBJECT or name == UNKNOWNBEING or level == 0 then return end
	local classification = UnitClassification(target)
	self.lastunit = namelevel
	if not DarkPortal_Save.mobs[name] then
		local class = UnitClass(target)
		mob = {
			desc = self:Concat(classification, class, UnitCreatureType(target), UnitCreatureFamily(target)),
			min = level,
			max = level,
		}
	else
		mob = DarkPortal_Save.mobs[name]
		if level > 0 and level > mob.max then mob.max = level end
		if level > 0 and (level < mob.min or mob.min < 0) then mob.min = level end
	end
	mob.seen = self:Get_Day()
	DarkPortal_Save.mobs[name] = mob
	if classification == "worldboss" then
		bosses = self:Get_Profile_Data("bosses")
		if not bosses[name] then
			bosses[name] = { zone = GetRealZoneText()	}
			self:Set_Profile_Data("bosses", bosses)
		end
	end
end

function DarkPortal:Set_Mob_Hit(msg)
	self.lastunithit = { time = time(), msg = msg }
end

function DarkPortal:Set_Pet()
	if UnitClass("player") == "Hunter" and HasPetUI() then
		local pets = self:Get_Profile_Data("pets")
		local name = UnitName("pet")
		pets[name] = {
			stable = 0,
			type = UnitCreatureFamily("pet"),
			experience = self:Concat(GetPetExperience()),
			strength = self:Concat(UnitStat("pet", 1)),
			agility = self:Concat(UnitStat("pet", 2)),
			stamina = self:Concat(UnitStat("pet", 3)),
			intellect = self:Concat(UnitStat("pet", 4)),
			spirit = self:Concat(UnitStat("pet", 5)),
			armor = self:Concat(UnitArmor("pet")),
			damage = self:Concat(UnitDamage("pet")),
		}
		self:Set_Profile_Data("pets", pets)
	end
end

function DarkPortal:Set_Pet_Abandon()
	local name = UnitName("pet")
	local pets = self:Get_Profile_Data("pets")
	pets[name] = nil
	self:Set_Profile_Data("pets", pets)
end

function DarkPortal:Set_Pet_Stable()
	if UnitClass("player") == "Hunter" then
		local pets = self:Get_Profile_Data("pets")
		for i = 0, GetNumStableSlots() do
			local icon, name, level, type, loyalty = GetStablePetInfo(i)
			if name then
				pets[name] = {
					icon = self:Get_Texture(icon),
					level = level,
					type = type,
					loyalty = loyalty,
					stable = 1,
				}
			end
		end
		self:Set_Profile_Data("pets", pets)
		self:Set_Pet()
	end
end

function DarkPortal:Set_Professions()
	local trade = GetCraftSkillLine(1)
	if trade and (trade ~= "" or trade ~= UNKNOWN) then
		local skills = self:Get_Profile_Data("tradeskills")
		skills[trade] = {}
		local header
		for i = 1, GetNumCrafts() do
			local name, rank, type = GetCraftInfo(i)
			if name and name ~= "" then
				if type == "header" then
					header = name
					skills[trade][header] = {}
				else
					local item = GetCraftDescription(i)
					local itemid = self:Get_Itemlink(GetCraftItemLink(i))
					if rank and rank ~= "" then name = name .. " (" .. rank .. ")" end
					if not item or item == "" then item = name end
					local numreagents = GetCraftNumReagents(i)
					local reagents = {}
					if numreagents > 0 then
						for j = 1, numreagents do
							local reagent = GetCraftReagentItemLink(i, j)
							if reagent then
								local _, _, count = GetCraftReagentInfo(i, j)
								local reagentid = self:Get_Itemlink(reagent)
								if count > 1 then
									table.insert(reagents, reagentid .. "x" .. count)
								else
									table.insert(reagents, reagentid)
								end
							end
						end
					end
					if header then
						skills[trade][header][name] = self:Concat(item, table.concat(reagents, ","), itemid)
					else
						skills[trade][name] = self:Concat(item, table.concat(reagents, ","), itemid)
					end
				end
			end
		end
		self:Set_Profile_Data("tradeskills", skills)
	end
end

function DarkPortal:Set_Profile()
	DarkPortal_Save.profiles[self.character] = {
		["locale"] = GetLocale(),
		["stats"] = {
			["sex"] = UnitSex("player"),
			["faction"] = UnitFactionGroup("player"),
			["race"] = UnitRace("player"),
			["class"] = UnitClass("player"),
		}
	}
end

function DarkPortal:Set_Quests()
	this:UnregisterEvent("UNIT_QUEST_LOG_CHANGED")
	if not self.character then return end
	local quests = {}
	local quested = self:Get_Profile_Data("quested")
	local old_quest = GetQuestLogSelection()
	local header = ""
	for i = 1, GetNumQuestLogEntries() do
		local title, level, tag, _, _, _, status = GetQuestLogTitle(i)
		title = string.gsub(title, "%[.+%]%s+", "") -- strip [60+] etc
		if title then
			if level == 0 then
				header = title
				quests[header] = {}
			else
				SelectQuestLogEntry(i)
				local desc, obj = GetQuestLogQuestText()

				local rewards = {}
				for j = 1, GetNumQuestLogRewards() do
					local itemlink = GetQuestLogItemLink("reward", j)
					if itemlink then
						local itemid = self:Get_Itemlink(itemlink)
						if itemid > 0 then table.insert(rewards, itemid) end
					end							
				end
				rewards = table.concat(rewards, "^")
				if rewards == "" then rewards = nil end
				
				local choices = {}
				for j = 1, GetNumQuestLogChoices() do
					local itemlink = GetQuestLogItemLink("choice", j)
					if itemlink then
						local itemid = self:Get_Itemlink(itemlink)
						if itemid > 0 then table.insert(choices, itemid) end
					end
				end
				choices = table.concat(choices, "^")
				if choices == "" then choices = nil end

				quests[header][title] = {
					["tag"] = tag,
					["level"] = level,
					["desc"] = self:Get_QuestText(desc),
					["obj"] = self:Get_QuestText(obj),
					["rewards"] = rewards,
					["choices"] = choices,
					["status"] = 0,
				}
				if status and status == 1 then
					quests[header][title]["status"] = 1
					local played = self:Get_Played_Time()
					if played > 0 then quested[title] = quested[title] or played end
				elseif status and status == -1 then
					quests[header][title]["status"] = -1
				end
			end
		end
	end
	SelectQuestLogEntry(old_quest)
	self:Set_Profile_Data("quests", quests)
	self:Set_Profile_Data("quested", quested)
	this:RegisterEvent("UNIT_QUEST_LOG_CHANGED")
end

function DarkPortal:Set_Reputation()
	local reputation, collapsed = {}, {}
	for i = 1, GetNumFactions() do
		local info = {GetFactionInfo(i)}
		local header = select(1, info)
		local isCollapsed = select(10, info)
		if isCollapsed then collapsed[header] = true end
	end
	ExpandFactionHeader(0)
	header = ""
	for i = 1, GetNumFactions() do
		local faction, _, _, _, _, rep, war, _, isHeader = GetFactionInfo(i)
		if faction then
			if isHeader then
				header = faction
				reputation[header] = reputation[header] or {}
			elseif header then
				reputation[header][faction] = self:Concat(rep, war)
			end
		end
	end
	self:Set_Profile_Data("reputation", reputation)
	for i = 1, GetNumFactions() do
		local header = GetFactionInfo(i)
		if collapsed[header] then CollapseFactionHeader(i) end
	end
end

function DarkPortal:Set_Session_Event(type, data) -- type = "loots" data = itemid, type = "kills" "deaths" data = "mob"
	if type ~= "loots" and type ~= "kills" and type ~= "deaths" then return nil end
	local sessionstamp = self:Get_Stamp()
	local sessions = self:Get_Profile_Data("sessions")
	local today = sessions[sessionstamp] or {}
	local session = today[type] or {}
	if not session[data] then session[data] = 1 else session[data] = session[data] + 1 end
	today[type] = session
	sessions[sessionstamp] = today
	self:Set_Profile_Data("sessions", sessions)
	return true
end


function DarkPortal:Set_Skills()
	local header
	local skills, skills2, abandoned = {}, {}, {}
	for i = 1, GetNumSkillLines() do
		local skill, isHeader, _, rank, _, modifier, rankmax = GetSkillLineInfo(i)
		if skill then
			if isHeader then
				header = skill
				skills[header] = {}
			elseif header then
				skills[header][skill] = self:Concat(rank, rankmax, modifier)
				table.insert(skills2, skill)
			end
		end
	end
	self:Set_Profile_Data("skills", skills)

	skills = self:Get_Profile_Data("tradeskills")
	for _, trade in pairs(skills) do
		local abandon = true
		for _, skill in ipairs(skills2) do
			if skill == trade then 
				abandon = nil
				break
			end
		end
		if abandon then table.insert(abandoned, trade) end
	end
	for _, skill in ipairs(abandoned) do skills[skill] = nil end
	self:Set_Profile_Data("tradeskills", skills)
end

function DarkPortal:Set_Spells()
	local spells = {}
	for tab = 1, GetNumSpellTabs() do
		local header, _, offset, numspells = GetSpellTabInfo(tab)
		spells[header] = {}
		for i = offset + 1, offset + numspells do
			local name, rank = GetSpellName(i, BOOKTYPE_SPELL)
			if rank and rank ~= "" then name = name .. " (" .. self:Trim(rank) .. ")" end
			DarkPortalTooltip:SetSpell(i, BOOKTYPE_SPELL)
			spells[header][name] = self:Concat(self:Get_Texture(GetSpellTexture(i, BOOKTYPE_SPELL)), self:Get_Tooltip())
		end
	end
	self:Set_Profile_Data("spells", spells)
end

function DarkPortal:Set_Stats()
	local stats = {
		-- Character
		level = UnitLevel("player"),
		health = UnitHealthMax("player"),
		experience = self:Concat(UnitXP("player"), UnitXPMax("player")),
		money = GetMoney(),
		played = self:Get_Played_Time(),

		-- Base stats
		strength = self:Concat(UnitStat("player", 1)),
		agility = self:Concat(UnitStat("player", 2)),
		stamina = self:Concat(UnitStat("player", 3)),
		intellect = self:Concat(UnitStat("player", 4)),
		spirit = self:Concat(UnitStat("player", 5)),
		armor = self:Concat(UnitArmor("player")),

		-- Defenses
		defense = self:Concat(UnitDefense("player")),
		dodge = GetDodgeChance(),
		parry = GetParryChance(),
		block = GetBlockChance(),

		-- Melee
		damage = self:Concat(UnitDamage("player")),
		attack = self:Concat(UnitAttackBothHands("player")),
		speed = self:Concat(UnitAttackSpeed("player")),
		ap = self:Concat(UnitAttackPower("player")),
		crit = GetCritChance(),

		-- Ranged
		rangeddamage = self:Concat(UnitRangedDamage("player")),
		rangedattack = self:Concat(UnitRangedAttack("player")),
		rangedap = self:Concat(UnitRangedAttackPower("player")),
		rangedcrit = GetRangedCritChance(),

		-- Resistances
		resists = {
			fire = self:Concat(UnitResistance("player", 2)),
			nature = self:Concat(UnitResistance("player", 3)),
			frost = self:Concat(UnitResistance("player", 4)),
			shadow = self:Concat(UnitResistance("player", 5)),
			arcane = self:Concat(UnitResistance("player", 6)),
		},

		-- Spell
		spellbonus = {
			fire = GetSpellBonusDamage(3),
			nature = GetSpellBonusDamage(4),
			frost = GetSpellBonusDamage(5),
			shadow = GetSpellBonusDamage(6),
			arcane = GetSpellBonusDamage(7),
		},
		spellcrit = {
			fire = GetSpellCritChance(3),
			nature = GetSpellCritChance(4),
			frost = GetSpellCritChance(5),
			shadow = GetSpellCritChance(6),
			arcane = GetSpellCritChance(7),
		},
		healbonus = GetSpellBonusHealing(),
		penetration = GetSpellPenetration(),

		-- PVP
		honortoday = self:Concat(GetPVPSessionStats()),
		honoryesterday = self:Concat(GetPVPYesterdayStats()),
		honor = self:Concat(GetPVPLifetimeStats()),
		arenapoints = GetArenaCurrency(),
	}
	if UnitPowerType("player") ~= 0 then
		stats.mana = -UnitPowerType("player")
	else
		stats.mana = UnitManaMax("player")
	end
	local hitrating = {}
	for i, rating in ipairs({"weapon", "defense", "dodge", "parry", "block", "hitmelee", "hitranged", "hitspell", "critmelee", "critranged", "critspell", "hittakenmelee", "hittakenranged", "hittakenspell", "crittakenmelee", "crittakenranged", "crittakenspell", "hastemelee", "hasteranged", "hastespell"}) do
		hitrating[rating] = self:Concat(GetCombatRating(i), GetCombatRatingBonus(i))
	end
	stats.hitrating = hitrating
	self:Set_Profile_Data("stats", stats)
end

function DarkPortal:Set_Talents()
	local loaded = LoadAddOn("Blizzard_TalentUI")
	if loaded then
		local talents = {}
		for tab = 1, GetNumTalentTabs() do
			local tabname = GetTalentTabInfo(tab)
			talents[tabname] = {}
			for i = 1, GetNumTalents(tab) do
				local talent, texture, posy, posx, rank, maxrank = GetTalentInfo(tab, i)
				if rank > 0 then
					DarkPortalTooltip:SetTalent(tab, i)
					talents[tabname][talent] = self:Concat(rank, maxrank, posx, posy, self:Get_Texture(texture), self:Get_Tooltip())
				end
			end
		end
		self:Set_Profile_Data("talents", talents)
	end
end

function DarkPortal:Set_Tradeskills()
	local trade = GetTradeSkillLine()
	if trade and (trade ~= "" or trade ~= UNKNOWN) then
		self:Set_Skills()
		local skills = self:Get_Profile_Data("tradeskills")
		skills[trade] = {}
		local header
		local numtradeskills = GetNumTradeSkills()
		for i = 1, numtradeskills do
			local name, type = GetTradeSkillInfo(i)
			if name and name ~= "" then
				if type == "header" then
					header = name
					skills[trade][header] = {}
				elseif header then
					local itemlink = GetTradeSkillItemLink(i)
					if itemlink then
						local itemid = self:Get_Itemlink(itemlink)
						if itemid then 
							local numreagents = GetTradeSkillNumReagents(i)
							local reagents = {}
							if numreagents > 0 then
								for j = 1, numreagents do
									local reagent = GetTradeSkillReagentItemLink(i, j)
									if reagent then
										local _, _, count = GetTradeSkillReagentInfo(i, j)
										local reagentid = self:Get_Itemlink(reagent)
										if count > 1 then
											table.insert(reagents, reagentid .. "x" .. count)
										else
											table.insert(reagents, reagentid)
										end
									end
								end
							end
							skills[trade][header][name] = self:Concat(itemid, table.concat(reagents, ","))
						end
					end
				end
			end
		end
		self:Set_Profile_Data("tradeskills", skills)
	end
end

function DarkPortal:Login()
	DarkPortalTooltip:Show()
	DarkPortalTooltip:SetOwner(UIParent, "ANCHOR_NONE")
	local version = GetAddOnMetadata("DarkPortal", "Version")
	if not DarkPortal_Save.settings or not DarkPortal_Save.settings.version or DarkPortal_Save.settings.version ~= version then
		DarkPortal_Save = {
			profiles = {},
			items = {},
			mobs = {},
			random = {},
			bosses = {},
		}
	end
	DarkPortal_Save.settings = {
		version = version,
		build = GetBuildInfo(),
		locale = GetLocale(),
	}
	self.character = self:Concat(GetRealmName(), UnitName("player"))
	if not DarkPortal_Save.profiles[self.character] then self:Set_Profile() end

	local level = UnitLevel("player")
	if level == 70 then
		this:RegisterEvent("ARENA_TEAM_UPDATE")
		this:RegisterEvent("ARENA_TEAM_ROSTER_UPDATE")
		for i = 1, MAX_ARENA_TEAMS do
			local team = GetArenaTeam(i)
			if team then
				PVPTeamDetails.team = i
				ArenaTeamRoster(i)
			end
		end
	end

	local class = UnitClass("player")
	if class == "Hunter" then
		this:RegisterEvent("PET_STABLE_CLOSED")
		hooksecurefunc("PetAbandon", function() DarkPortal:Set_Pet_Abandon() end)
	end
	
	hooksecurefunc("Logout", function() DarkPortal:Logout() end)
	hooksecurefunc("Quit", function() DarkPortal:Logout() end)
end

function DarkPortal:Logout()
	if self.character and not self.quitted then
		self.quitted = true
		self:Set_Stats()
		self:Set_Buffs()
		self:Set_Equipment()
		self:Set_Inventory()
		self:Set_Skills()
		self:Set_Spells()
		self:Set_Talents()
		self:Set_Reputation()
		self:Set_Guild()
		self:Set_Quests()
		self:Set_Pet()
		self:Set_Profile_Data("datastamp", self:Get_Stamp())
		self:Archive()
	end
end

function DarkPortal:OnEvent()
	if event == "CHAT_MSG_COMBAT_CREATURE_VS_SELF_HITS" then
		self:Set_Mob_Hit(arg1)
	elseif event == "UPDATE_MOUSEOVER_UNIT" then
		if UnitExists("mouseover") then self:Set_Mob("mouseover") end
	elseif event == "PLAYER_TARGET_CHANGED" then
		if UnitExists("target") then self:Set_Mob("target") end
	elseif event == "CHAT_MSG_LOOT" then
		self:Set_Loot(arg1)
	elseif event == "CHAT_MSG_COMBAT_HOSTILE_DEATH" then
		self:Set_Kill(arg1)
	elseif event == "UNIT_QUEST_LOG_CHANGED" then
		if arg1 == "player" then self:Set_Quests() end
	elseif event == "BANKFRAME_CLOSED" then
		self:Set_Bank()
	elseif event == "TRADE_SKILL_SHOW" then
		self:Set_Skills()
		self:Set_Tradeskills()
	elseif event == "CRAFT_SHOW" then
		self:Set_Skills()
		self:Set_Professions()
	elseif event == "TIME_PLAYED_MSG" then
		self.session = { 
			played = arg1, 
			session = 0,
		}
	elseif event == "PLAYER_DEAD" then
		self:Set_Death()
	elseif event == "PLAYER_LEVEL_UP" then
		self:Set_Level(arg1)
--		if arg1 == 70
--			frame:RegisterEvent("ARENA_TEAM_UPDATE")
--			frame:RegisterEvent("ARENA_TEAM_ROSTER_UPDATE")
--		end
	elseif event == "ARENA_TEAM_UPDATE" or event == "ARENA_TEAM_ROSTER_UPDATE" then
		self:Set_Arena()
	elseif event == "PET_STABLE_CLOSED" then
		self:Set_Pet_Stable()
	elseif event == "PLAYER_ENTERING_WORLD" then
		this:UnregisterEvent(event)
		self.lootscans = self:Get_Scans(self.lootscans)
		self.combatscans = self:Get_Scans(self.combatscans)
		self.killscans = self:Get_Scans(self.killscans)
		if not self.session.session then
			self.session.session = 0
			RequestTimePlayed()
		end
		self:ChatMsg("|c" .. self.chatcolor .. "Dark Portal " .. DarkPortal_Save.settings.version .. " loaded")
	elseif event == "PLAYER_LOGIN" then
		self:Login()
	end
end

function DarkPortal:OnUpdate()
	self.session.session = self.session.session + arg1
end
		

-- initializing

local frame = CreateFrame("Frame")
frame:RegisterEvent("BANKFRAME_CLOSED")
frame:RegisterEvent("CHAT_MSG_COMBAT_CREATURE_VS_SELF_HITS")
frame:RegisterEvent("CHAT_MSG_COMBAT_HOSTILE_DEATH")
frame:RegisterEvent("CHAT_MSG_LOOT")
frame:RegisterEvent("CRAFT_SHOW")
frame:RegisterEvent("PLAYER_DEAD")
frame:RegisterEvent("PLAYER_LEVEL_UP")
frame:RegisterEvent("PLAYER_LOGIN")
frame:RegisterEvent("PLAYER_ENTERING_WORLD")
frame:RegisterEvent("PLAYER_TARGET_CHANGED")
frame:RegisterEvent("TIME_PLAYED_MSG")
frame:RegisterEvent("TRADE_SKILL_SHOW")
frame:RegisterEvent("UNIT_QUEST_LOG_CHANGED")
frame:RegisterEvent("UPDATE_MOUSEOVER_UNIT")
frame:SetScript("OnEvent", function(this, event, ...) DarkPortal:OnEvent(event, ...) end)
frame:SetScript("OnUpdate", function() DarkPortal:OnUpdate() end)

CreateFrame("GameTooltip", "DarkPortalTooltip", UIParent, "GameTooltipTemplate")
